package descentFly.away3D4.net
{
	import UIFree.ObjectEvent;
	import UIFree.uif_Timer;
	
	import descentFly.app.df_events;
	import descentFly.app.managePanel.chat.df_chat;
	import descentFly.away3D4.chips.df_chipAway;
	import descentFly.away3D4.df_awaySpace;
	import descentFly.away3D4.df_baseSpaceElement;
	import descentFly.chips.df_chip;
	import descentFly.controller.df_userController;
	import descentFly.data.df_chips;
	import descentFly.df_space;
	import descentFly.df_utils;
	import descentFly.server.df_servers;
	
	import flash.events.Event;
	import flash.events.TimerEvent;
	import flash.geom.Matrix3D;
	import flash.utils.Timer;
	
	import org.osmf.events.TimeEvent;
	
	public class df_netSupport extends df_baseSpaceElement {
		public static const FIELD_ENTERUSER: String 		= 'enterUser';
		public static const FIELD_EXITUSER: String 			= 'exitUser';
		public static const FIELD_RECEIVEMESSAGE: String 	= 'receiveMessage';
		
		public static const ENTER_SESSION: String 			= 'ENTER_SESSION';
		public static const EXIT_SESSION: String 			= 'EXIT_SESSION';
		public static const SET_TIMER: String 				= 'SET_TIMER';
		public static const SET_POINTSSTATE: String 		= 'SET_POINTSSTATE';
		public static const UPDATE_POINTSSTATE: String 		= 'UPDATE_POINTSSTATE';
		public static const NF_POINTSSTATE: String 			= mySocket.NF_POINTSSTATE;
		
		public static const NETACTIONNOSUPPORT: Array = [
			df_userController.ACTION_WEAPONTNEXT,
			df_userController.ACTION_WEAPONTPREV,
		];
		
		private var _chips		: Object;
		private var _myChip		: df_chipAway;
		private var _socket		: mySocket;
		private var _acDelta	: Array;
		private var _deltaCount	: int;
		private var _deltaTimer	: Timer;
		private var _cTimer		: Timer;
		
		public function df_netSupport(a_space:df_awaySpace, a_options:Object) {
			super(a_space, a_options);
		}
		
		override protected function initialize():void {
			super.initialize();
			_chips = new Object();
			_deltaTimer = new Timer(150, 1);
			_deltaTimer.addEventListener(TimerEvent.TIMER_COMPLETE, doDeltaTimer);
			
//			Space.callWaitEvent2(Space.myChip != null, df_space.SETMYCHIP, enterSession);
			Space.addEventListener(df_space.SETMYCHIP, doSetMyChip);
			Space.app.stage.addEventListener(df_events.SENDTOCHAT, doSendToChat);
			createSocket();
		}
		
		protected function doSetMyChip(e: Event): void {
			newMyChip();
		}
		
		protected function newMyChip(): void {
			myChipNull();
			if (Space.myChipAway && (_myChip != Space.myChipAway)) {
				
				_myChip = Space.myChipAway;
				listenChipEvents();
				
				_socket.initChip(_myChip);
			}
		}
		
		protected function myChipNull(): void {
			if (_myChip) {
				removeChipEvents();
				_myChip = null;
			}
		}
		
		protected function removeChipEvents(): void {
			if (_myChip) {
				_myChip.removeEventListener(df_userController.ACTION, doAction);
				_myChip.removeEventListener(df_chip.DESTRUCTION, doDestruction);
			}
		}
		
		protected function listenChipEvents(): void {
			if (_myChip) {
				_myChip.addEventListener(df_userController.ACTION, doAction);
				_myChip.addEventListener(df_chip.DESTRUCTION, doDestruction);
			}
		}
		
		protected function createSocket(): void {
			options.socket.uid = Space.player.UID;
			_socket = new mySocket(options.socket.uid, options.socket.session);
			_socket.addEventListener(Event.CONNECT, connectHandler);
			_socket.addEventListener(Event.COMPLETE, doComplete);
			attemptConnect();
		}
		
		protected function attemptConnect(): void {
			_socket.connect(options.socket.host, options.socket.port);
		}
		
		protected function connectHandler(e: Event): void {
			_socket.removeEventListener(Event.CONNECT, connectHandler);
			Space.dispatchEvent(new Event(df_events.BATTLE_CONNECT));
			newMyChip();
		}
		
		protected function doDestruction(e: ObjectEvent): void {
			myChipNull();
			_socket.destructionMyChip(getUid(e.object as df_netChip));
		}
		
		protected function doComplete(e: ObjectEvent): void {
			if (e.object) {
				for (var uid: String in e.object.users) {
					var user: Object = e.object.users[uid];
					createNetChip(uid, options.defaultChip);
					setState(uid, user.MATRIX, user.LIVE, user.KEYSTATE);
				}
				listenSessionEvents();
				Space.view.stage.dispatchEvent(new ObjectEvent(ENTER_SESSION, e.object));
			}
		}
		
		protected function listenSessionEvents(): void {
			Space.addEventListener(SET_POINTSSTATE, doSetPointsState);
			Space.addEventListener(UPDATE_POINTSSTATE, doUpdatePointsState);
			
			_socket.addEventListener(Event.CLOSE, doCloseSession);
			_socket.addEventListener(mySocket.RESPONSE_NOTIFY, doNotify);
		}
		
		protected function removeSessionEvents(): void {
			Space.removeEventListener(SET_POINTSSTATE, doSetPointsState);
			Space.removeEventListener(UPDATE_POINTSSTATE, doUpdatePointsState);
			
			_socket.removeEventListener(Event.COMPLETE, doComplete);
			_socket.removeEventListener(Event.CLOSE, doCloseSession);
			_socket.removeEventListener(mySocket.RESPONSE_NOTIFY, doNotify);
		}
		
		protected function doSetPointsState(e: ObjectEvent): void {
			_socket.sendObject({
				method	: 'setPointsState',
				params	: e.object
			});
		}
		
		protected function doUpdatePointsState(e: ObjectEvent): void {
			_socket.sendObject({
				method	: 'updatePointsState',
				params	: e.object
			});
		}
		
		protected function setState(uid: String, matrix: Array, live: Number, keyState: Object): void {
			var chip: df_chipAway = getChip(uid);
			
			chip.callWaitEvent2(chip.body != null, df_chip.INITCONTROLLER, function (): void {
				var obj: Object = df_utils.matToObject(df_utils.arrToMat(matrix))
				chip.body.currentState.position = obj.position;
				chip.body.setOrientation(obj.orientation);
				chip.setLive(live);
				if (keyState)
					for (var i: String in keyState) {
						if (NETACTIONNOSUPPORT.indexOf(i) == -1) {
							chip.dispatchEvent(new ObjectEvent(df_userController.ACTION_REQUIRE, {
								action	: i,
								state	: keyState[i]
							}));
						}
					}
			})
		}
		
		protected function getChip(uid: String): df_netChip{
			return _chips[uid] as df_netChip;
		}
		
		protected function getUid(chip: df_netChip): String{
			if (!chip) return '';
			
			for (var uid: String in _chips)
				if (_chips[uid] == chip) return uid;
			return null;
		}
		
		protected function doNotify(e: ObjectEvent): void {
			var obj: Object = e.object;
			if (obj.notify == mySocket.NF_UPDATEKS) {
				var keyState: Object = new Object();
				keyState[obj.params.key] = obj.params.value;
				setState(obj.params.uid, obj.params.matrix, obj.params.live, keyState)
			} else if (obj.notify == mySocket.NF_INITIALIZE) {
				createNetChip(obj.params.uid, options.defaultChip);
				setState(obj.params.uid, obj.params.matrix, obj.params.live, obj.params.keyState);
				Space.app.stage.dispatchEvent(new ObjectEvent(df_events.ENTERUSER, obj.params));
				playSound(FIELD_ENTERUSER);
			} else if (obj.notify == mySocket.NF_DISPOSE) {
				getChip(obj.params.uid).dispose();
				Space.app.stage.dispatchEvent(new ObjectEvent(df_events.EXITUSER, obj.params))
				playSound(FIELD_EXITUSER);
			} else if (obj.notify == mySocket.NF_DESTRUCTION) {
				getChip(obj.params.uid).netDestruction();
				if (obj.params.killer)
					Space.app.stage.dispatchEvent(new ObjectEvent(df_events.KILLER, obj.params));
			}  else if (obj.notify == mySocket.NF_TIMER) {
				Space.dispatchEvent(new ObjectEvent(TimerEvent.TIMER, obj.params));
			}  else if (obj.notify == mySocket.NF_POINTSSTATE) {
				Space.dispatchEvent(new ObjectEvent(NF_POINTSSTATE, obj.params));
			}  else if (obj.notify == mySocket.NF_SENDCHAT) {
				Space.app.stage.dispatchEvent(new ObjectEvent(df_events.RECEIVECHATMESSAGE, obj.params));
				playSound(FIELD_RECEIVEMESSAGE);
			} 
		}
		
		protected function doSendToChat(e: ObjectEvent): void {
			_socket.sendObject({
				method	: 'chat',
				params	: e.object
			})
		}
		
		protected function doAction(e: ObjectEvent): void {
			if (NETACTIONNOSUPPORT.indexOf(e.object.action) == -1) {
				if (e.object.action == df_userController.ACTION_ROTATE) addDelta(e.object.state);
				else _socket.updateAction(e.object.action, e.object.state);
			}
		}
		
		protected function addDelta(delta: Array): void {
			if (!_acDelta) {
				_acDelta 	= [0, 0, 0];
				_deltaCount	= 0;
			}
			if (delta) {
				_acDelta[0] += delta[0];
				_acDelta[1] += delta[1];
				_acDelta[2] += delta[2];
				_deltaCount++;
				if (!_deltaTimer.running) _deltaTimer.start();
			} else {
				if (_acDelta[0] || _acDelta[1] || _acDelta[2]) {
					if (_deltaTimer.running) _deltaTimer.stop();
					sendCurDelta();
				}
				_socket.updateAction(df_userController.ACTION_ROTATE, 0);
			}
		}
		
		protected function sendCurDelta(): void {
/*			_acDelta[0] = _acDelta[0] / _deltaCount;
			_acDelta[1] = _acDelta[1] / _deltaCount;
			_acDelta[2] = _acDelta[2] / _deltaCount;*/
			_socket.updateAction(df_userController.ACTION_ROTATE, _acDelta);
			_acDelta[0] = 0;
			_acDelta[1] = 0;
			_acDelta[2] = 0;
			_deltaCount = 0;
		}
		
		protected function doDeltaTimer(e: TimerEvent): void {
			sendCurDelta();
		}
		
		protected function doCloseSession(e: Event=null): void {
			_socket.close();
			closeSession();
			attemptConnect();
		}
		
		protected function closeSession(): void {
			myChipNull();
			removeSessionEvents();
			Space.view.stage.dispatchEvent(new Event(EXIT_SESSION));
		}
		
		override protected function removeEvents():void {
			super.removeEvents();
			Space.removeEventListener(df_space.SETMYCHIP, doSetMyChip);
			Space.app.stage.removeEventListener(df_events.SENDTOCHAT, doSendToChat);
			_deltaTimer.removeEventListener(TimerEvent.TIMER, doDeltaTimer);
		}
		
		override public function dispose():void {
			if (_socket && _socket.connected) {
				_socket.close();
				closeSession();
			}
			if (_deltaTimer.running) _deltaTimer.stop();
			super.dispose();
			_socket = null;
		}
		
		protected function createNetChip(uid: String, chipIndexClass: int): void { //a_class: Class, a_options: Object): void {
			var obj: Object = df_chips.list[chipIndexClass];
			_chips[uid] = new (obj.Class)(Space, obj.options);
		}

		protected function removeNetChip(uid: String): void {
			_chips[uid].dispose();
			delete(_chips[uid]);
		}
	}
}